﻿module game.battle.projectiles.fireprojectiles;

import std.range: isForwardRange;

import gamelib.types;
import gamelib.graphics.renderer2d.renderer;

import game.units;
import game.transformmapper;

struct SimpleFireProjectile
{
    pos_t pos;
    pos_t speed;
    int size = 3;
    static immutable maxTicks = 10;
    int ticksRemaining = maxTicks;

    void kill() pure nothrow
    {
        ticksRemaining = 0;
    }
    
    void update() pure nothrow
    {
        --ticksRemaining;
        pos += speed;
    }

    @property bool isAlive() const pure nothrow { return ticksRemaining > 0; }

    struct ProjectileRenderer
    {
        private Rect[] mRects;

        void draw(T)(Renderer ren, in TransformMapper mapper, T range) if(isForwardRange!T)
        {
            if(range.empty)
            {
                return;
            }
            
            if(range.length > mRects.length)
            {
                mRects.length = calcCacheSize(range.length);
            }
            
            int i = 0;
            foreach(p;range)
            {
                auto pt = mapper.map(p.pos);
                mRects[i] = Rect(pt.x - p.size, pt.y - p.size, 1 + 2*p.size, 1 + 2*p.size);
                ++i;
            }
            ren.drawColor = Color(255,255,127);
            ren.drawFilledRects(mRects[0..i]);
        }
    }
}

struct ExpandingFireProjectile
{
    pos_t pos;
    pos_t speed;
    int size = 1;
    static immutable minHoleSize = 10;
    static immutable maxTicks = 20;
    int ticksRemaining = maxTicks;

    void kill() pure nothrow
    {
        ticksRemaining = 0;
    }
    
    void update() pure nothrow
    {
        --ticksRemaining;
        pos += speed;
        ++size;
    }

    @property bool isAlive() const pure nothrow { return ticksRemaining > 0; }
    
    struct ProjectileRenderer
    {
        private Rect[] mRects;
        
        void draw(T)(Renderer ren, in TransformMapper mapper, T range)
        {
            if(range.empty)
            {
                return;
            }
            
            if(range.length * 4 > mRects.length)
            {
                mRects.length = calcCacheSize(range.length * 4);
            }
            
            int i = 0;
            foreach(p;range)
            {
                auto pt = mapper.map(p.pos);
                if(p.size <= minHoleSize)
                {
                    mRects[i] = Rect(pt.x - p.size, pt.y - p.size, 1 + 2*p.size, 1 + 2*p.size);
                    ++i;
                }
                else
                {
                    auto w1 = 1 + p.ticksRemaining * minHoleSize / maxTicks;
                    auto w2 = 1 + 2 * p.size - minHoleSize;
                    mRects[i+0] = Rect(pt.x - p.size     , pt.y - p.size     , w1, w2);
                    mRects[i+1] = Rect(pt.x - p.size     , pt.y - p.size + w2, w2, w1);
                    mRects[i+2] = Rect(pt.x - p.size + w2, pt.y - p.size + w1, w1, w2);
                    mRects[i+3] = Rect(pt.x - p.size + w1, pt.y - p.size     , w2, w1);
                    i += 4;
                }
            }
            ren.drawColor = Color(255,255,127);
            ren.drawFilledRects(mRects[0..i]);
        }
    }
}

struct SmokeProjectile
{
    pos_t pos;
    pos_t speed;
    int size = 1;
    int type = 0;
    static immutable minHoleSize = 5;
    static immutable maxTicks = 20;
    int ticksRemaining = maxTicks;

    void kill() pure nothrow
    {
        ticksRemaining = 0;
    }

    void update() pure nothrow
    {
        --ticksRemaining;
        pos += speed;
        ++size;
    }

    @property bool isAlive() const pure nothrow { return ticksRemaining > 0; }
    
    struct ProjectileRenderer
    {
        private Rect[] mRects;
        
        void draw(T)(Renderer ren, in TransformMapper mapper, T range)
        {
            if(range.empty)
            {
                return;
            }
            
            if(range.length * 2 > mRects.length)
            {
                mRects.length = calcCacheSize(range.length * 2);
            }
            
            int i = 0;
            foreach(p;range)
            {
                auto pt = mapper.map(p.pos);
                auto sz = p.size >> 1;
                if(sz <= minHoleSize)
                {
                    mRects[i] = Rect(pt.x - sz, pt.y - sz, 1 + 2*sz, 1 + 2*sz);
                    ++i;
                }
                else
                {
                    auto w1 = 1 + p.ticksRemaining * minHoleSize / maxTicks;
                    auto w2 = 1 + 2 * sz - minHoleSize;
                    if(0 == p.type)
                    {
                        mRects[i+0] = Rect(pt.x - sz     , pt.y - sz, w1, w2);
                        mRects[i+1] = Rect(pt.x - sz + w2, pt.y - sz, w1, w2);
                    }
                    else
                    {
                        mRects[i+0] = Rect(pt.x - sz, pt.y - sz     , w2, w1);
                        mRects[i+1] = Rect(pt.x - sz, pt.y - sz + w2, w2, w1);
                    }
                    i += 2;
                }
            }
            ren.drawColor = Color(127,127,127);
            ren.drawFilledRects(mRects[0..i]);
        }
    }
}

